/**
 * 此处为个人手写实现一些es6的数组方法
 *     包括 map filter foreach some every reduce
 *         includes flat
 */

// let arrayPrototype = Array.prototype;
// console.log(arrayPrototype);

// map 
function mymap (cb, thisArg) {
    let resArr = [];
    const _this = this;
    let hereThis = thisArg ? thisArg : _this;
    // 错误提示
    // 使用 Object.prototype.toString.call 来检测cb类型可检测出箭头函数， typeof 则不行
    if (Object.prototype.toString.call(cb).slice(8, -1) !== 'Function') throw new Error(`${cb} is not a function`);
    _this.forEach((item, index) => {
        resArr.push(cb.call(hereThis, item, index, _this)) // 将回调结果放入数组中
    })
    return resArr // 返回map后的数组
}

Array.prototype.mymap = mymap;

// filter
function myfilter (cb, thisArg) {
    let resArr = [];
    const _this = this;
    let hereThis = thisArg ? thisArg : _this;
    // 错误提示
    // 使用 Object.prototype.toString.call 来检测cb类型可检测出箭头函数， typeof 则不行
    if (Object.prototype.toString.call(cb).slice(8, -1) !== 'Function') throw new Error(`${cb} is not a function`);
    _this.forEach((item, index) => {
        if (cb.call(hereThis, item, index, _this)) resArr.push(item);
    })
    return resArr // 返回map后的数组
}
Array.prototype.myfilter = myfilter;

// forEach
function myforeach (cb, thisArg) {
    const _this = this;
    let hereThis = thisArg ? thisArg : _this;
    // 错误提示
    // 使用 Object.prototype.toString.call 来检测cb类型可检测出箭头函数， typeof 则不行
    if (Object.prototype.toString.call(cb).slice(8, -1) !== 'Function') throw new Error(`${cb} is not a function`);
    let length = _this.length;
    for (let index = 0; index < length; index++) {
        cb.call(hereThis, _this[index], index, _this);
    }
}
Array.prototype.myforeach = myforeach;

// some
function mysome (cb, thisArg) {
    const _this = this;
    let hereThis = thisArg ? thisArg : _this;
    // 错误提示
    // 使用 Object.prototype.toString.call 来检测cb类型可检测出箭头函数， typeof 则不行
    if (Object.prototype.toString.call(cb).slice(8, -1) !== 'Function') throw new Error(`${cb} is not a function`);
    let flag = false;
    let length = _this.length;
    for (let index = 0; index < length; index++) {
        if(cb.call(hereThis, _this[index], index, _this)) {
            flag = true;
            break;
        }
    }
    return flag;
}
Array.prototype.mysome = mysome;

// every
function myevery (cb, thisArg) {
    const _this = this;
    let hereThis = thisArg ? thisArg : _this;
    // 错误提示
    // 使用 Object.prototype.toString.call 来检测cb类型可检测出箭头函数， typeof 则不行
    if (Object.prototype.toString.call(cb).slice(8, -1) !== 'Function') throw new Error(`${cb} is not a function`);
    let flag = true;
    let length = _this.length;
    for (let index = 0; index < length; index++) {
        if(!cb.call(hereThis, _this[index], index, _this)) {
            flag = false;
            break;
        }
    }
    return flag;
}
Array.prototype.myevery = myevery;

// reduce
function myreduce (cb, initialValue) {
    // const _this = this;
    // 错误提示
    // 使用 Object.prototype.toString.call 来检测cb类型可检测出箭头函数， typeof 则不行
    if (Object.prototype.toString.call(cb).slice(8, -1) !== 'Function') throw new Error(`${cb} is not a function`);
    // 设置第一个值的初始值，有两种情况
    // 1, initialValue 不存在时， 第一个值为数值的第一项，第二个值为数组的第二项
    // 2, initialValue 存在时，第一个值为 initialValue ，第二个值为数组的第一项
    let accumulator = initialValue || 0; 
    this.forEach((item, index, arr) => {
        accumulator = cb(accumulator, item, index, arr);
    });
    return accumulator;
}
Array.prototype.myreduce = myreduce;

// includes 此方法与 indexOf 的不同在于，它可以检测出数组中的 NaN
function myincludes (searchElement, fromIndex = 0) {
    const arr = this;
    const length = arr.length;
    // 错误提示
    if (typeof fromIndex !== 'number') throw new Error(`${fromIndex} is not a number`);
    // 初始化起始点
    let start = Math.max(fromIndex >= 0 ? fromIndex : length + fromIndex, 0);
    while (start < length) {
        if (check(searchElement, arr[start])) return true;
    }
    return false;

    function check (searchElement, target) {
        return searchElement === target || (isNaN(searchElement) && isNaN(target));
    }
}
Array.prototype.myincludes = myincludes;

// flat
function myflat (depth = 1) {
    const arr = this;
    // 错误提示
    if (typeof depth !== 'number') throw new Error(`${fromIndex} is not a number`);
    depth = Math.floor(depth);
    function flatDeep (arr, depth) {
        return depth > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatDeep(val, depth - 1) : val), []) : arr.slice();
    }
    return flatDeep(arr, depth);
}
Array.prototype.myflat = myflat;
/**
 * let arr = [1,2,3,[4,5,[6,7,8]]];
 * 以下降为数组将一维的方式
 * arr.flat()     arr.reduce((prve, curr) => prve.concat(curr), [])   [].concat(...arr)
 */
